package com.hemerasolutions.v2trainer.component.table;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;

import org.primefaces.event.CellEditEvent;
import org.primefaces.model.DefaultTreeNode;
import org.primefaces.model.TreeNode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;

import com.hemerasolutions.v2trainer.component.BaseComponent;
import com.hemerasolutions.v2trainer.component.interfaces.table.IntensityZoneTableCompInterface;
import com.hemerasolutions.v2trainer.dao.interfaces.IntensityZoneDaoInterface;
import com.hemerasolutions.v2trainer.model.exercise.executions.AssociationET;
import com.hemerasolutions.v2trainer.model.exercise.executions.Execution;
import com.hemerasolutions.v2trainer.model.exercise.intensity.IntensityExecution;
import com.hemerasolutions.v2trainer.model.exercise.intensity.IntensityTable;
import com.hemerasolutions.v2trainer.model.exercise.intensity.IntensityZone;
import com.hemerasolutions.v2trainer.model.modality.Modality;
import com.hemerasolutions.v2trainer.model.training.Training;
import com.hemerasolutions.v2trainer.model.unit.intensity.IntensityUnit;
import com.hemerasolutions.v2trainer.model.unit.intensity.Rpe;
import com.hemerasolutions.v2trainer.service.interfaces.ModalityServiceInterface;


@Controller
@Component("intensityZoneTableComp")
@Scope("prototype")
public class IntensityZoneTableComp extends BaseComponent implements IntensityZoneTableCompInterface {

	private static final long serialVersionUID = 1L;
	
	private IntensityTable intensityTable;

	private IntensityExecution intensityExecution;
	
	private IntensityZone selIntensityZone;
	
	private Modality modality;

    private TreeNode rootNode;
    
    private List<Rpe> fullRpes;
    
    @Autowired
    private ModalityServiceInterface modalityService;
	
    @Autowired
    private IntensityZoneDaoInterface izDao;
    
	@Override
	public void refresh() {
		this.createAvailableColumns(this.intensityExecution.getExecution(), this.intensityTable);
		this.fullRpes = modalityService.listAllRpes();
	}

	
	
	@Override
	public void onCellChange(CellEditEvent event){

		this.selIntensityZone = this.intensityTable.getIntensitys().get(event.getRowIndex());
		
		if(!this.zoneCanBeChanged()){
			this.intensityTable.getIntensitys().set(event.getRowIndex(),izDao.getById(selIntensityZone.getId()));
    		this.showUsedZoneError();
    	}
		
	}

	@Override
	public void toExecutionTimeDialog(ActionEvent ev){

		this.selIntensityZone = (IntensityZone)ev.getComponent().getAttributes().get("intensityZone");
		
    	if(this.zoneCanBeChanged()){
    		String component = (String)ev.getComponent().getAttributes().get("component");
    		String dialog	= (String)ev.getComponent().getAttributes().get("dialog");
    		String atcomplete = (String)ev.getComponent().getAttributes().get("atcomplete");
    		
    		this.updatePFComponnent( component + ":" + dialog);
    		this.executeJavascript(atcomplete);
    		
    	}else{
    		this.showUsedZoneError();
    	}

	}
	
	private void createAvailableColumns( Execution ex, IntensityTable it) {

    	
		TreeNode availableColumns = new DefaultTreeNode("Root", null);
        TreeNode perfomance = new DefaultTreeNode(this.localizedText("Performance"), availableColumns);
        TreeNode subject = new DefaultTreeNode(this.localizedText("Subjectives"), availableColumns);
        TreeNode physic = new DefaultTreeNode(this.localizedText("Physiologic"), availableColumns);
        
        availableColumns.setExpanded(true);


        if(ex.getAssociationETs() != null){
        	for(AssociationET assoc: ex.getAssociationETs()){
        		if(assoc.getTrainingType().getId() == this.modality.getTrainingType().getId()){
            		for( IntensityUnit iu: assoc.getIntensityUnits()){
            			if (!it.getIntensityUnits().contains(iu)){
            				if(iu.getUnitType() == IntensityUnit.IU_TYPE_PERFORMANCE){
                       			new DefaultTreeNode("column", new ColumnModel(this.localizedText(iu.getName()), iu.getName()), perfomance);
            				}else if(iu.getUnitType() == IntensityUnit.IU_TYPE_SUBJECTIVES){
                       			new DefaultTreeNode("column", new ColumnModel(this.localizedText(iu.getName()), iu.getName()), subject);
            				}else if(iu.getUnitType() == IntensityUnit.IU_TYPE_PHYSIOLOGIC){
                       			new DefaultTreeNode("column", new ColumnModel(this.localizedText(iu.getName()), iu.getName()), physic);
            				}
            			}
            		}
        		}
        	}
    	}
    	rootNode = availableColumns;
    }	

	@Override
	public void treeToTable() {

		if(this.zonesCanBeChanged()){
			Map<String,String> params = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap();
	        String unit = params.get("unit");


			this.intensityTable.addIntensityUnit(this.intensityExecution.getExecution(), this.modality.getTrainingType(), unit);
			
			//remove from nodes
	    	TreeNode root = this.rootNode;


	    	
	    	for(TreeNode nodeAux : root.getChildren()) {
	    		for(TreeNode node : nodeAux.getChildren()){
	        		ColumnModel model = (ColumnModel) node.getData();
	                if(model.getProperty().equals(unit)) {
	                	nodeAux.getChildren().remove(node);
	                    break;
	                }
	    		}
	    	}
		}else{
    		this.showUsedZoneError();
    	}

    }
	
    @Override
	public void tableToTree() {
		if(this.zonesCanBeChanged()){
	    	
			Map<String,String> params = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap();
			String unit = params.get("unit");
	
		
			for (int i = 0; i < intensityTable.getIntensityUnits().size(); i++) {
			  IntensityUnit iu = intensityTable.getIntensityUnits().get(i);
			  if(iu.getName().equals(unit)){
				  for (IntensityZone iz : intensityTable.getIntensitys()) {
					  iz.getIntensitys().remove(i);
				  }
				  
				  new DefaultTreeNode("column", new ColumnModel(this.localizedText(iu.getName()), iu.getName()), this.rootNode.getChildren().get(iu.getUnitType()));
				  intensityTable.getIntensityUnits().remove(i);
				  break;
			  }
			}
		}else{
    		this.showUsedZoneError();
    	}	
    }
 	
    @Override
	public void toAddItensityZone(){
    	if(zonesCanBeChanged()){
        	this.setSelIntensityZone(this.intensityTable.addIntensityZone());
        	if(this.getSelIntensityZone().getPhysicalCapacity() == null){
        		this.getSelIntensityZone().setPhysicalCapacity(this.modality.getTrainingType().getPhysicalCapacitys().get(0));
        	}
    	}else{
    		this.showUsedZoneError();
    	}
    }

    @Override
	public void upItensityZone(){
    	if(this.zonesCanBeChanged()){
    		this.intensityTable.upIntensityZone(this.selIntensityZone);
    		
    	}else{
    		this.showUsedZoneError();
    	}
    }	
    
    @Override
	public void downItensityZone(){
    	if(this.zonesCanBeChanged()){
    		this.intensityTable.downIntensityZone(this.selIntensityZone);
    	}else{
    		this.showUsedZoneError();
    	}
    }

    @Override
	public void deleteItensityZone(){

    	if(this.zonesCanBeChanged()){
        	this.intensityTable.getIntensitys().remove(this.selIntensityZone);
    	}else{
    		this.showUsedZoneError();
    	}
    	
    }


	/**
	* Retorna falso sempre que qualquer zona da tabela de zonas estiver sendo usada,
	* já que ao eliminar qualquer zona a ordem da zonas será alterada modificando os
	* calculos das métricas para o treinos existentes, além de gerar erros em exercicios agrupados
	* que cruzam zonas da modalidade com zona do exercício.
	 */
    @Override
    public boolean zonesCanBeChanged() {

    	List<Training> trainings = new ArrayList<Training>();
    	List<IntensityZone> izs  = new ArrayList<IntensityZone>();
    	
    	for(IntensityZone iz:this.intensityTable.getIntensitys()){
    		if(iz.getId() != null){
    			izs.add(iz);
    		}
    	}
    	
    	if(izs.isEmpty()){
    		return true;
    	}else{
        	trainings = modalityService.trainingsUsingIzs(izs);
    	}
    	
    	if(!trainings.isEmpty()){
    		return false;
    	}else{
        	return true;
    	}
    	
	}

    
    /**
	* Retorna falso sempre que qualquer zona da tabela de zonas estiver sendo usada,
	* já que ao eliminar qualquer zona a ordem da zonas será alterada modificando os
	* calculos das métricas para o treinos existentes, além de gerar erros em exercicios agrupados
	* que cruzam zonas da modalidade com zona do exercício.
	 */
    @Override
    public boolean zoneCanBeChanged() {

    	if(this.selIntensityZone.getId() == null){
    		return true;
    	}else{
        	if(!modalityService.trainingsUsing(this.selIntensityZone).isEmpty()){
        		return false;
        	}else{
            	return true;
        	}
    	}
    	
	}


    @Override
    public void showUsedZoneError(){

    	List<Training> trainings = modalityService.trainingsUsing(this.selIntensityZone);

    	for(Training training:trainings){
			if(training.isTrainingSystem()){
	    		this.showErrorMessage(this.localizedText("ErrorOnChange"), this.localizedText("ThisIntensityZoneDefinitionIsUsedIn") 
	    								+ this.localizedText("TrainingSystem") + training.getName());
				
			}else{
	    		this.showErrorMessage(this.localizedText("ErrorOnChange"), this.localizedText("ThisIntensityZoneDefinitionIsUsedIn") 
										+ this.localizedText("Training") + training.getName());

			}
		}
    	
    }

   
	@Override
	public IntensityTable getIntensityTable() {
		return intensityTable;
	}


	@Override
	public void setIntensityTable(IntensityTable intensityTable) {
		this.intensityTable = intensityTable;
	}


	@Override
	public IntensityZone getSelIntensityZone() {
		return selIntensityZone;
	}


	@Override
	public void setSelIntensityZone(IntensityZone selIntensityZone) {
		this.selIntensityZone = selIntensityZone;
	}


	@Override
	public Modality getModality() {
		return modality;
	}


	@Override
	public void setModality(Modality modality) {
		this.modality = modality;
	}


	@Override
	public IntensityExecution getIntensityExecution() {
		return intensityExecution;
	}


	@Override
	public void setIntensityExecution(IntensityExecution intensityExecution) {
		this.intensityExecution = intensityExecution;
	}


	@Override
	public TreeNode getRootNode() {
		return rootNode;
	}


	@Override
	public void setRootNode(TreeNode rootNode) {
		this.rootNode = rootNode;
	}  
	
	@Override
	public List<Rpe> getFullRpes() {
		return fullRpes;
	}

	@Override
	public void setFullRpes(List<Rpe> fullRpes) {
		this.fullRpes = fullRpes;
	}




	static public class ColumnModel implements Serializable {
	  	  
		private static final long serialVersionUID = 1L;
		private String header;
        private String property;
  
        public ColumnModel(String header, String property) {
            this.header = header;
            this.property = property;
        }
  
        public String getHeader() {
            return header;
        }
  
        public String getProperty() {
            return property;
        }
	}	
	

}
